<?php
/**
 * Aero - Xistins Technologies PHP Framework
 * 
 * LICENSE
 * This source file is subject to the New BSD license that is bundled 
 * with this package in the LICENSE.txt. It is also available through 
 * the world-wide-web at this URL: http://dev.xistins.com/license/production
 * If you did not receive a copy of this license and are unable to obtain
 * it through the world-wide-web, please send an email to 
 * license@xistins.com so we can send you a copy immediately.
 * 
 * @category   Aero
 * @package    Aero_Db
 * @subpackage Adapter
 * @copyright  http://dev.xistins.com/license/production     New BSD
 * @version    $Id: Adapter.php 2 2009-11-03 04:10:03Z csouth@xistins.com $
 */

/**
 * Abstract database adapter
 * 
 * @category   Aero
 * @package    Aero_Db
 * @subpackage Adapter
 * @copyright  http://dev.xistins.com/license/production     New BSD
 * @version    $Id: Adapter.php 2 2009-11-03 04:10:03Z csouth@xistins.com $
 */
abstract class Aero_Db_Adapter
{
    /**
     * Adapter configuration settings
     * @var array()
     */
    protected $_config = array();

    /**
     * Database connection
     * @var object|resource|null
     */
    protected $_connection = null;

    /**
     * Default Statement Class
     * @var string
     */
    protected $_defaultStatementClass = 'Aero_Db_Statement';

    /**
     * Adapter construct
     * 
     * @param  array $config
     * @return void
     * @throws Aero_Db_Exception for none array $config
     */
    public function __construct($config = null)
    {
        if(null !== $config && !is_array($config)) {
            Aero_Loader::loadClass('Aero_Db_Exception');
            throw new Aero_Db_Exception('The configuration provided to Aero_Db_Adapter::__construct()
                                         must be an array.');
        }

        $this->_checkRequired($config);
        $this->_runConfig($config);
    }

    /**
     * Check for required elements of the configuration
     * 
     * @param  array $config
     * @return null
     * @throws Aero_Db_Exception for missing elements
     */
    protected function _checkRequired(array $config)
    {
        // Database Name
        if(!array_key_exists('dbname', $config)) {
            Aero_Loader::loadClass('Aero_Db_Exception');
            throw new Aero_Db_Exception('Adapter configuration needs to have a "dbname" key.');
        }

        // Username
        if(!array_key_exists('username', $config)) {
            Aero_Loader::loadClass('Aero_Db_Exception');
            throw new Aero_Db_Exception('Adapter configuration needs to have a "username" key.');
        }

        // Password
        if(!array_key_exists('dbname', $config)) {
            Aero_Loader::loadClass('Aero_Db_Exception');
            throw new Aero_Db_Exception('Adapter configuration needs to have a "password" key.');
        }
    }

    /**
     * Run configuration
     * 
     * @param  array $config
     * @return Aero_Db_Abstract
     */
    protected function _runConfig(array $config)
    {
        foreach($config as $key => $value) {
            switch(strtolower($key)) {
                case 'defaultstatementclass':
                    $this->setDefaultStatementClass($value);
                    unset($config[$key]);
                    break;
                default:
                    break;
            }
        }

        $this->_config = $config;
        return $this;
    }

    /**
     * Get database connection
     * 
     * @return object|resource|null
     */
    public function getConnection()
    {
        if(null === $this->_connection) {
            $this->_connect();
        }

        return $this->_connection;
    }

    /**
     * Close connection
     * 
     * @return void
     */
    public function closeConnection()
    {
        $this->_connection = null;
    }

    /**
     * Begin transaction
     * 
     * @return Aero_Db_Adapter
     */
    public function beginTransaction()
    {
        $this->getConnection();
        $this->_beginTransation();
        return $this;
    }

    /**
     * Commit a transaction
     * 
     * @return Aero_Db_Adapter
     */
    public function commit()
    {
        $this->getConnection();
        $this->_commit();
        return $this;
    }

    /**
     * Rollback a transaction
     * 
     * @return Aero_Db_Adapter
     */
    public function rollBack()
    {
        $this->getConnection();
        $this->_rollBack();
        return $this;
    }

    /**
     * Turn a string into SQL safe quote
     * 
     * @param  string $variable
     * @return string
     */
    public function getSafeQuote($variable)
    {
        return '"' . mysql_real_escape_string($variable) . '"';
    }

    /**
     * Get default statement class
     * 
     * @return string
     */
    public function getDefaultStatementClass()
    {
        return $this->_defaultStatementClass;
    }

    /**
     * Set default statement class
     * 
     * @param  string $class
     * @return Aero_Db_Adapter
     */
    public function setDefaultStatementClass($class)
    {
        $this->_defaultStatementClass = $class;
        return $this;
    }

    /**
     * Get a statment class
     * 
     * @param  string $query
     * @return Aero_Db_Statement
     */
    public function getStatement($query)
    {
        $this->getConnection();
        $stmtClass = $this->getDefaultStatementClass();
        Aero_Loader::loadClass($stmtClass);
        return new $stmtClass($this, $query);
    }

    /**
     * Run query
     * 
     * @param  string $sql
     * @return Aero_Db_Statement
     */
    public function query($sql)
    {
        $stmt = $this->getStatement($sql);
        $stmt->execute();
        return $stmt;
    }

    /**
     * --------------------------------------------------------------------
     * Everything below this point is abstract functions that adapters need
     * to have for regular functionality.
     * --------------------------------------------------------------------
     */

    /**
     * Creates database connection
     */
    abstract protected function _connect();

    /**
     * Returns a list of the tables in the database returned as an array
     */
    abstract public function listTables();

    /**
     * Adapter specific code for beginning a transaction
     */
    abstract protected function _beginTransaction();

    /**
     * Adapter specific code for commiting a transaction
     */
    abstract protected function _commit();

    /**
     * Adapter specific code for rolling back a transaction
     */
    abstract protected function _rollBack();
}